
#ifndef __ODBCRECORDSET_H__
#define __ODBCRECORDSET_H__

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

/*************************************************************************
 FILE :		  ODBCRecordset.h

 Author :	  Stefan Tchekanov  (stefant@iname.com)

 Description: CODBCRecordset and CDBField classes definition.

			  CODBCRecordset extends CRecordset in a way it handles
			  an array of CDBField objects that are mapped to the
			  corresponding recordset columns. It uses CRecordset
			  data exchange mechanism. You would use CODBCRecordset
			  in a similar way as you would use CRecordset subclasses
			  created by ClassWizard except CODBCRecordset does
			  not have hardcoded the number and the type of database
			  fields.
			  Added some functionality for easy data retrieval.

			  CDBField extends CDBVariant with appropriate
			  assignment operators and some other useful methods.

Copyright(c) 1998,1999,2000
by Stefan Tchekanov (stefant@iname.com)

This code may be used in compiled form in any way you desire. This
file may be redistributed unmodified by any means PROVIDING it is
not sold for profit without the authors written consent, and
providing that this notice and the authors name is included. If
the source code in this file is used in any commercial application
then a simple email to the author would be nice.

This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability if it causes any damage.

*************************************************************************/
/* #    Revisions    # */


/////////////////////////////////////////////////////////////////////////////

#include <afxdb.h>
#include <afxtempl.h>


inline
void	BinaryToString( CString& cStr, const CLongBinary& bin, bool bExpand )
{
    int	nSize = min( (int)bin.m_dwDataLength, cStr.GetLength() );
    if ( bExpand )
        nSize = bin.m_dwDataLength;

    if ( nSize < 1 )
    {
        cStr.Empty();
        return;
    }

    void*	p = ::GlobalLock( bin.m_hData );
    if ( p == NULL )
        ::AfxThrowMemoryException();

    char* pStr = cStr.GetBuffer( nSize );
    memcpy( pStr, p, nSize );
    cStr.ReleaseBuffer( nSize );
    ::GlobalUnlock( bin.m_hData );
}
/////////////////////////////////////////////////////////////////////////////
//	CDBField class is extension to CDBVariant.
//	supports copy constructor and many other useful features
/////////////////////////////////////////////////////////////////////////////

class	CDBField : public CDBVariant
{

//	Constructors are private to avoid instantiation
//	except by CODBCRecordset
private:
    CDBField()
    {
        m_bIsNull = true;
    }
    CDBField( const CDBField& dbv )
    {
        m_bIsNull = true;
    }

    CDBField& operator =( const CDBField& dbv );

    virtual ~CDBField()
    {}



public:
    //	Assignment operators




////////////////////////////////////////////////////////////////
//	Assignment operators
////////////////////////////////////////////////////////////////

    CDBField& operator =( const bool bVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = bVal;
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (bVal) ? 'T' : 'F';
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (bVal) ? 1 : 0;
            return	*this;

        case	DBVT_LONG:
            m_lVal = (bVal) ? 1 : 0;
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)((bVal) ? 1.0 : 0.0);
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)((bVal) ? 1.0 : 0.0);
            return	*this;

        case	DBVT_DATE:
            //	Cannot convert to datetime
            ASSERT( FALSE );
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            m_pstring->Format( "%c", (bVal) ? 'T' : 'F' );
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const char chVal )
    {
        return	operator =( (unsigned char) chVal );
    }
    CDBField& operator =( const unsigned char chVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = (chVal == 'T' || chVal == '1');
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (unsigned char)chVal;
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (short)chVal;
            return	*this;

        case	DBVT_LONG:
            m_lVal = (long)chVal;
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)chVal;
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)chVal;
            return	*this;

        case	DBVT_DATE:
            //	Cannot convert to datetime
            ASSERT( FALSE );
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            m_pstring->Format( "%c", chVal );
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const short sVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = (sVal != 0);
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (unsigned char)sVal;
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (short)sVal;
            return	*this;

        case	DBVT_LONG:
            m_lVal = (long)sVal;
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)sVal;
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)sVal;
            return	*this;

        case	DBVT_DATE:
            //	Cannot convert to datetime
            ASSERT( FALSE );
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            m_pstring->Format( "%hd", sVal );
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const int iVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = (iVal != 0);
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (unsigned char)iVal;
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (short)iVal;
            return	*this;

        case	DBVT_LONG:
            m_lVal = (long)iVal;
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)iVal;
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)iVal;
            return	*this;

        case	DBVT_DATE:
            //	Cannot convert to datetime
            ASSERT( FALSE );
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            m_pstring->Format( "%d", iVal );
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const long lVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = (lVal != 0);
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (unsigned char)lVal;
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (short)lVal;
            return	*this;

        case	DBVT_LONG:
            m_lVal = (long)lVal;
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)lVal;
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)lVal;
            return	*this;

        case	DBVT_DATE:
            //	Cannot convert to datetime
            ASSERT( FALSE );
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            m_pstring->Format( "%ld", lVal );
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const float fltVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = (fltVal != 0.0);
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (unsigned char)fltVal;
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (short)fltVal;
            return	*this;

        case	DBVT_LONG:
            m_lVal = (long)fltVal;
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)fltVal;
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)fltVal;
            return	*this;

        case	DBVT_DATE:
            //	Cannot convert to datetime
            ASSERT( FALSE );
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            m_pstring->Format( "%f", fltVal );
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const double dblVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = (dblVal != 0.0);
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (unsigned char)dblVal;
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (short)dblVal;
            return	*this;

        case	DBVT_LONG:
            m_lVal = (long)dblVal;
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)dblVal;
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)dblVal;
            return	*this;

        case	DBVT_DATE:
            //	Cannot convert to datetime
            ASSERT( FALSE );
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            m_pstring->Format( "%f", dblVal );
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const COleDateTime& dtVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
        case	DBVT_UCHAR:
        case	DBVT_SHORT:
        case	DBVT_LONG:
        case	DBVT_SINGLE:
        case	DBVT_DOUBLE:
            //	Cannot convert to the current data type
            ASSERT( FALSE );
            return	*this;

        case	DBVT_DATE:
            ASSERT( m_pdate != NULL );
            m_pdate->year	= dtVal.GetYear();
            m_pdate->month	= dtVal.GetMonth();
            m_pdate->day	= dtVal.GetDay();
            m_pdate->hour	= dtVal.GetHour();
            m_pdate->minute	= dtVal.GetMinute();
            m_pdate->second = dtVal.GetSecond();
            m_pdate->fraction = 0;
            return	*this;

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            *m_pstring = dtVal.Format();
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
    CDBField& operator =( const CString& cVal )
    {
        return	operator =( (LPCTSTR)cVal );
    }
    CDBField& operator =( const LPCTSTR szVal )
    {
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            //	Undefined data type
            ASSERT( FALSE );

        case	DBVT_BOOL:
            m_boolVal = (szVal != NULL || atoi( szVal ) != 0 );
            return	*this;

        case	DBVT_UCHAR:
            m_chVal = (unsigned char)szVal[0];
            return	*this;

        case	DBVT_SHORT:
            m_iVal = (short)atoi( szVal );
            return	*this;

        case	DBVT_LONG:
            m_lVal = (long)atol( szVal );
            return	*this;

        case	DBVT_SINGLE:
            m_fltVal = (float)atof( szVal );
            return	*this;

        case	DBVT_DOUBLE:
            m_dblVal = (double)atof( szVal );
            return	*this;

        case	DBVT_DATE:
        {
            ASSERT( m_pdate != NULL );
            COleDateTime	dt;
            dt.ParseDateTime( szVal );
            m_pdate->year	= dt.GetYear();
            m_pdate->month	= dt.GetMonth();
            m_pdate->day	= dt.GetDay();
            m_pdate->hour	= dt.GetHour();
            m_pdate->minute	= dt.GetMinute();
            m_pdate->second = dt.GetSecond();
            m_pdate->fraction = 0;
            return	*this;
        }

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            *m_pstring = szVal;
            return	*this;

        case	DBVT_BINARY:
            //	CRecordset does not support writing to CLongBinary fields
            ASSERT( FALSE );
            return	*this;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	*this;
    }
////////////////////////////////////////////////////////////////



    //	Get the bool value. Do appropriate conversion.
    bool			AsBool()	const

    {
        if ( IsNull() )
        return	false;

        CString	cValue;
        switch ( m_dwType )
        {
        case	DBVT_NULL:
            return	false;

        case	DBVT_BOOL:
            return	(m_boolVal == TRUE);

            case	DBVT_UCHAR:
                return	(m_chVal == 'T' || m_chVal == '1');

            case	DBVT_SHORT:
                return	(m_iVal != 0);

            case	DBVT_LONG:
                return	(m_lVal != 0);

            case	DBVT_SINGLE:
                return	(m_fltVal != 0.0);

            case	DBVT_DOUBLE:
                return	(m_dblVal != 0.0);

            case	DBVT_DATE:
                //	Cannot convert date to bool
                ASSERT( FALSE );
                break;

            case	DBVT_STRING:
                ASSERT( m_pstring != NULL );
                if ( m_pstring->GetLength() < 1 )
                    return	false;
                return	((*m_pstring)[0] == 'T' || (*m_pstring)[0] == '1');

            case	DBVT_BINARY:
                //	Cannot convert long binary to bool
                ASSERT( FALSE );
                break;
            }
        //	Undefined data type
        ASSERT( FALSE );
        return	false;
    }

    //	Get the unsigned char value. Do appropriate conversion.
    unsigned char	AsChar()	const

    {
        if ( IsNull() )
        return	' ';

        switch ( m_dwType )
        {
        case	DBVT_NULL:
            return	' ';

        case	DBVT_BOOL:
            return	(m_boolVal) ? 'T' : 'F';

            case	DBVT_UCHAR:
                return	m_chVal;

            case	DBVT_SHORT:
                return	(unsigned char)m_iVal;

            case	DBVT_LONG:
                return	(unsigned char)m_lVal;

            case	DBVT_SINGLE:
                return	(unsigned char)m_fltVal;

            case	DBVT_DOUBLE:
                return	(unsigned char)m_dblVal;

            case	DBVT_DATE:
                //	Cannot convert date to unsigned char
                ASSERT( FALSE );
                break;

            case	DBVT_STRING:
                ASSERT( m_pstring != NULL );
                if ( m_pstring->GetLength() < 1 )
                    return	' ';
                return	(unsigned char)((*m_pstring)[0]);

            case	DBVT_BINARY:
                //	Cannot convert long binary to unsigned char
                ASSERT( FALSE );
                break;
            }
        //	Undefined data type
        ASSERT( FALSE );
        return	' ';
    }

    //	Get the short value. Do appropriate conversion.
    short			AsShort()	const

    {
        if ( IsNull() )
        return	0;

        switch ( m_dwType )
        {
        case	DBVT_NULL:
            return	0;

        case	DBVT_BOOL:
            return	m_boolVal ? 1 : 0;

        case	DBVT_UCHAR:
            return	(short)m_chVal;

            case	DBVT_SHORT:
                return	m_iVal;

            case	DBVT_LONG:
                return	(short)m_lVal;

            case	DBVT_SINGLE:
                return	(short)m_fltVal;

            case	DBVT_DOUBLE:
                return	(short)m_dblVal;

            case	DBVT_DATE:
                //	Cannot convert date to short
                ASSERT( FALSE );
                break;

            case	DBVT_STRING:
                ASSERT( m_pstring != NULL );
                return	(short)atoi( *m_pstring );

            case	DBVT_BINARY:
                //	Cannot conver long binary to short
                ASSERT( FALSE );
                break;
            }
        //	Undefined data type
        ASSERT( FALSE );
        return	0;
    }

    //***	Get the int value which is equal to long value. Do appropriate conversion.
    int				AsInt()		const

    {
        return	AsLong();
    }

    //	Get the long value. Do appropriate conversion.
    long			AsLong()	const

    {
        if ( IsNull() )
        return	0;

        switch ( m_dwType )
        {
        case	DBVT_NULL:
            return	0;

        case	DBVT_BOOL:
            return	m_boolVal ? 1 : 0;

        case	DBVT_UCHAR:
            return	(long)m_chVal;

            case	DBVT_SHORT:
                return	(long)m_iVal;

            case	DBVT_LONG:
                return	m_lVal;

            case	DBVT_SINGLE:
                return	(long)m_fltVal;

            case	DBVT_DOUBLE:
                return	(long)m_dblVal;

            case	DBVT_DATE:
                //	Cannot convert date to long
                ASSERT( FALSE );
                break;

            case	DBVT_STRING:
                ASSERT( m_pstring != NULL );
                return	atol( *m_pstring );

            case	DBVT_BINARY:
                //	Cannot conver long binary to long
                ASSERT( FALSE );
                break;
            }
        //	Undefined data type
        ASSERT( FALSE );
        return	0;
    }

    //	Get the float value. Do appropriate conversion.
    float			AsFloat()	const

    {
        if ( IsNull() )
        return	0.0;

        switch ( m_dwType )
        {
        case	DBVT_NULL:
            return	0.0;

        case	DBVT_BOOL:
            return	(float)(m_boolVal ? 1.0 : 0.0);

            case	DBVT_UCHAR:
                return	(float)m_chVal;

            case	DBVT_SHORT:
                return	(float)m_iVal;

            case	DBVT_LONG:
                return	(float)m_lVal;

            case	DBVT_SINGLE:
                return	m_fltVal;

            case	DBVT_DOUBLE:
                return	(float)m_dblVal;

            case	DBVT_DATE:
                //	Cannot convert date to float
                ASSERT( FALSE );
                break;

            case	DBVT_STRING:
                ASSERT( m_pstring != NULL );
                return	(float)atof( *m_pstring );

            case	DBVT_BINARY:
                //	Cannot conver long binary to float
                ASSERT( FALSE );
                break;
            }
        //	Undefined data type
        ASSERT( FALSE );
        return	0.0;
    }

    //	Get the double value. Do appropriate conversion.
    double			AsDouble()	const

    {
        if ( IsNull() )
        return	0.0;

        switch ( m_dwType )
        {
        case	DBVT_NULL:
            return	0.0;

        case	DBVT_BOOL:
            return	m_boolVal ? 1.0 : 0.0;

        case	DBVT_UCHAR:
            return	(double)m_chVal;

            case	DBVT_SHORT:
                return	(double)m_iVal;

            case	DBVT_LONG:
                return	(double)m_lVal;

            case	DBVT_SINGLE:
                return	(double)m_fltVal;

            case	DBVT_DOUBLE:
                return	m_dblVal;

            case	DBVT_DATE:
                //	Cannot convert date to double
                ASSERT( FALSE );
                break;

            case	DBVT_STRING:
                ASSERT( m_pstring != NULL );
                return	atof( *m_pstring );

            case	DBVT_BINARY:
                //	Cannot conver long binary to double
                ASSERT( FALSE );
                break;
            }
        //	Undefined data type
        ASSERT( FALSE );
        return	0.0;
    }
    //	Get the date and time value. Do appropriate conversion.
    COleDateTime	AsDate()	const

    {
        COleDateTime	date;
        if ( IsNull() )
    {
        date.SetStatus( COleDateTime::null );
            return	date;
        }

        switch ( m_dwType )
    {
    case	DBVT_NULL:
        date.SetStatus( COleDateTime::null );
            return	date;

        case	DBVT_BOOL:
            date.SetStatus( COleDateTime::invalid );
            return	date;

        case	DBVT_UCHAR:
            date.SetStatus( COleDateTime::invalid );
            return	date;

        case	DBVT_SHORT:
            return	COleDateTime( (time_t)m_iVal );

        case	DBVT_LONG:
            return	COleDateTime( (time_t)m_lVal );

        case	DBVT_SINGLE:
            return	COleDateTime( (time_t)m_fltVal );

        case	DBVT_DOUBLE:
            return	COleDateTime( (time_t)m_dblVal );

        case	DBVT_DATE:
            ASSERT( m_pdate != NULL );
            return	COleDateTime(	m_pdate->year, m_pdate->month, m_pdate->day,
                                 m_pdate->hour, m_pdate->minute, m_pdate->second );

        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            date.ParseDateTime( *m_pstring );
            return	date;

        case	DBVT_BINARY:
            //	Cannot conver long binary to date
            ASSERT( FALSE );
            break;
        }
        //	Undefined data type
        ASSERT( FALSE );
        date.SetStatus( COleDateTime::invalid );
        return	date;
    }

    //	Get the CString value. Do appropriate conversion.
    CString			AsString()	const

    {
        CString	cValue;

        switch ( m_dwType )
    {
    case	DBVT_NULL:
        return	cValue;

    case	DBVT_BOOL:
        return	CString( m_boolVal ? "T" : "F" );

        case	DBVT_UCHAR:
            return	CString( m_chVal );

        case	DBVT_SHORT:
            cValue.Format( "%hd", m_iVal );
            return	cValue;

        case	DBVT_LONG:
            cValue.Format( "%ld", m_lVal );
            return	cValue;

        case	DBVT_SINGLE:
            cValue.Format( "%f", m_fltVal );
            return	cValue;

        case	DBVT_DOUBLE:
            cValue.Format( "%f", m_dblVal );
            return	cValue;

        case	DBVT_DATE:
        {
            ASSERT( m_pdate != NULL );
            COleDateTime	date( m_pdate->year, m_pdate->month, m_pdate->day,
                               m_pdate->hour, m_pdate->minute, m_pdate->second );
            return	date.Format();
        }
        case	DBVT_STRING:
            ASSERT( m_pstring != NULL );
            return	*m_pstring;

        case	DBVT_BINARY:
            ASSERT( m_pbinary != NULL );
            ::BinaryToString( cValue, *m_pbinary, true );
            return	cValue;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	cValue;
    }
    //	Get the long binary value. Do appropriate conversion.
    CLongBinary*	AsBinary()	const
    {
        switch ( m_dwType )
    {
    case	DBVT_NULL:
        return	NULL;

    case	DBVT_BOOL:
    case	DBVT_UCHAR:
    case	DBVT_SHORT:
    case	DBVT_LONG:
    case	DBVT_SINGLE:
    case	DBVT_DOUBLE:
    case	DBVT_DATE:
    case	DBVT_STRING:
        //	Cannot convert to long binary
        ASSERT( FALSE );
            break;

        case	DBVT_BINARY:
            return	m_pbinary;
        }
        //	Undefined data type
        ASSERT( FALSE );
        return	m_pbinary;
    }


    //	Data type conversion operators

    operator	bool() const;
    operator	unsigned char() const;
    operator	short() const;
    operator	int() const;
    operator	long() const;
    operator	float() const;
    operator	double() const;
    operator	COleDateTime() const;
    operator	CString() const;
    operator	CLongBinary*() const;

    //	Field type query methods

    bool	IsNull()	const
    {
        return m_bIsNull || m_dwType == DBVT_NULL;
    };
    bool	IsBool()	const
    {
        return m_dwType == DBVT_BOOL;
    };
    bool	IsChar()	const
    {
        return m_dwType == DBVT_UCHAR;
    };
    bool	IsShort()	const
    {
        return m_dwType == DBVT_SHORT;
    };
    bool	IsInt()		const
    {
        return m_dwType == DBVT_LONG;
    };
    bool	IsLong()	const
    {
        return m_dwType == DBVT_LONG;
    };
    bool	IsFloat()	const
    {
        return m_dwType == DBVT_SINGLE;
    };
    bool	IsDouble()	const
    {
        return m_dwType == DBVT_DOUBLE;
    };
    bool	IsNumber()	const
    {
        return IsShort() || IsLong() || IsFloat() || IsDouble();
    };
    bool	IsDate()	const
    {
        return m_dwType == DBVT_DATE;
    };
    bool	IsString()	const
    {
        return m_dwType == DBVT_STRING;
    };
    bool	IsBinary()	const
    {
        return m_dwType == DBVT_BINARY;
    };

    //	Get the field name
    const	CString&	GetName()	const;

private:
    bool		m_bIsNull;	//	If this field contain a NULL value
    CString		m_cName;	//	Contain the field (column) name

    void	SetNull( bool bIsNull )
    {
        m_bIsNull = bIsNull;
    };

    friend	class	CODBCRecordset;
};
/////////////////////////////////////////////////////////////////////////////
inline
const	CString&	CDBField::GetName()	const
{
    return	m_cName;
}

inline
CDBField::operator	bool() const
{
    return	AsBool();
}
inline
CDBField::operator	unsigned char() const
{
    return	AsChar();
}
inline
CDBField::operator	short() const
{
    return	AsShort();
}
inline
CDBField::operator	int() const
{
    return	AsInt();
}
inline
CDBField::operator	long() const
{
    return	AsLong();
}
inline
CDBField::operator	float() const
{
    return	AsFloat();
}
inline
CDBField::operator	double() const
{
    return	AsDouble();
}
inline
CDBField::operator	COleDateTime() const
{
    return	AsDate();
}
inline
CDBField::operator	CString() const
{
    return	AsString();
}
inline
CDBField::operator	CLongBinary*() const
{
    return	NULL;
}
/////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////
//	CODBCRecordset
/////////////////////////////////////////////////////////////////////////////

class	CODBCRecordset : public CRecordset
{
public:
    // Our constructor uses CRecordset constructor
    CODBCRecordset( CDatabase* pDatabase = NULL ) :
            CRecordset( pDatabase )
    {
        m_fields = NULL;
        m_AllocatedFields = 0;
    }
    virtual	~CODBCRecordset()
    {
        Clear();
    }

    virtual	BOOL	Open( LPCTSTR lpszSQL,
                       UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE,
                       DWORD dwOptions = 0 )

    {
        //	Allocate the maximum possible field info storage
        //	This is managed by CRecordset class
        m_nFields = 255;
        m_bNotLoadedFieldsMap = true;
        BOOL	nRes = CRecordset::Open(
                        nOpenType,
                        lpszSQL,
                        dwOptions );

        return	nRes;
    }

    virtual	void	Move( long nRows, WORD wFetchType = SQL_FETCH_RELATIVE )// throw( CDBException, CMemoryException );

    {
        if ( m_bNotLoadedFieldsMap )
        {
            LoadFieldNamesMap();
            m_bNotLoadedFieldsMap = false;
        }
        CRecordset::Move( nRows, wFetchType );
    }


    //	Number of fields could be get by
    //	CRecordset::GetODBCFieldCount() method


    //	New functions:

    //	Get the field ID by name - case insensitive
    //	CRecordset::GetFieldIndexByName() works,
    //	but is case sensitive
    int			GetFieldID( LPCTSTR szName )

    {
        void*	idx = (void*)-1;
        CString	cName( szName );
        cName.MakeUpper();
        if ( ! m_mapNameIdx.Lookup( cName, idx ) )
            return	-1;
        return	(int)idx;
    }

    //	Get the field name by ID
    CString		GetFieldName( int nField )

    {
        //	The field ID is invalid
        ASSERT( nField >= 0 );
        ASSERT( nField < GetODBCFieldCount() );

        CString		cName;
        void*		idx;
        POSITION	pos = m_mapNameIdx.GetStartPosition();
        while ( pos != NULL )
        {
            m_mapNameIdx.GetNextAssoc( pos, cName, idx );
            if ( (int)idx == nField )
                return	cName;
        }
        cName.Empty();
        return	cName;
    }


    //	Gets a field by name
    CDBField&	Field( LPCTSTR szName );
    //	Gets a field by number
    CDBField&	Field( int nField );

    //	Gets a field by name
    CDBField&	operator()( LPCTSTR szField );
    //	Gets a field by number
    CDBField&	operator()( int nField );


    //	Get values by field names. Do conversion as appropriate.

    bool			GetBool( LPCTSTR szName );
    unsigned char	GetChar( LPCTSTR szName );
    short			GetShort( LPCTSTR szName );
    int				GetInt( LPCTSTR szName );
    long			GetLong( LPCTSTR szName );
    float			GetFloat( LPCTSTR szName );
    double			GetDouble( LPCTSTR szName );
    COleDateTime	GetDate( LPCTSTR szName );
    CString			GetString( LPCTSTR szName );
    CLongBinary*	GetBinary( LPCTSTR szName );


    //	Get values by field number. Do conversion as appropriate.

    bool			GetBool( int nCol );
    unsigned char	GetChar( int nCol );
    short			GetShort( int nCol );
    int				GetInt( int nCol );
    long			GetLong( int nCol );
    float			GetFloat( int nCol );
    double			GetDouble( int nCol );
    COleDateTime	GetDate( int nCol );
    CString			GetString( int nCol );
    CLongBinary*	GetBinary( int nCol );


private:
    CDBField*			m_fields;			//	Storage for fields
    int					m_AllocatedFields;	//	Used to make smart storage reallocation
    CMapStringToPtr		m_mapNameIdx;		//	Map field names to their ID
    bool				m_bNotLoadedFieldsMap;	//	Is field names map still not loaded

    void	LoadFieldNamesMap()

    {
        m_mapNameIdx.RemoveAll();

        int	nFields = m_nFields = GetODBCFieldCount();

        //	Smart storage reallocation for the fields buffer
        if ( m_AllocatedFields < nFields )
        {
            Clear();
            m_fields = new CDBField[ m_nFields ];
            m_AllocatedFields = m_nFields;
        }

        //	Load field names map
        CODBCFieldInfo	fi;
        CString			cName;
        for ( int i = 0; i < nFields; i++ )
        {
            //	Clear the previously allocated storage object
            m_fields[i].Clear();

            // Determine the field type and initialize the data buffer
            GetODBCFieldInfo( i, fi );
            AllocDataBuffer( m_fields[i], fi );

            //	Set the field name
            fi.m_strName.MakeUpper();
            cName = fi.m_strName;

            //	Make different field names for the fields with
            //	equal names.
            int	fldCount = 1;
            while ( GetFieldID( cName ) != -1 )
            {
                fldCount++;
                cName.Format( "%s%d", fi.m_strName, fldCount );
            }
            m_fields[i].m_cName = cName;
            m_mapNameIdx.SetAt( cName, (void*)i );
        }
    }

    //	Clear the internal data structures
    void	Clear()
    {
        if ( m_fields != NULL )
            delete[]	m_fields;
        m_fields = NULL;
        m_AllocatedFields = 0;
    }

    //	RFX support
    virtual	void	DoFieldExchange( CFieldExchange* pFX )

    {
        pFX->SetFieldType( CFieldExchange::outputColumn );


        CString		cFieldName;
        for ( UINT i = 0; i < m_nFields; i++ )
        {
            cFieldName.Format( "[%s]", GetFieldName(i) );
            switch ( m_fields[i].m_dwType )
            {
            case	DBVT_NULL:
                break;
            case	DBVT_BOOL:
                RFX_Bool( pFX, cFieldName, m_fields[i].m_boolVal );
                break;
            case	DBVT_UCHAR:
                RFX_Byte( pFX, cFieldName, m_fields[i].m_chVal );
                break;
            case	DBVT_SHORT:
                //	CDBVariant::m_iVal is of type short
                //	RFX_Int() requires parameter of type int.
                //	Class wizard maps int variable in this case
                //	but CDBVariand does not have int member.
                m_fields[i].m_dwType = DBVT_LONG;
                RFX_Long( pFX, cFieldName, m_fields[i].m_lVal );
                break;
            case	DBVT_LONG:
                RFX_Long( pFX, cFieldName, m_fields[i].m_lVal );
                break;
            case	DBVT_SINGLE:
                RFX_Single( pFX, cFieldName, m_fields[i].m_fltVal );
                break;
            case	DBVT_DOUBLE:
                RFX_Double( pFX, cFieldName, m_fields[i].m_dblVal );
                break;
            case	DBVT_DATE:
                RFX_Date( pFX, cFieldName, *m_fields[i].m_pdate );
                break;
            case	DBVT_STRING:
            {
                CODBCFieldInfo	fi;
                GetODBCFieldInfo( i, fi );
                RFX_Text( pFX, cFieldName, *m_fields[i].m_pstring, fi.m_nPrecision );
                break;
            }
            case	DBVT_BINARY:
                RFX_LongBinary( pFX, cFieldName, *(m_fields[i].m_pbinary) );
                break;
            default:
                //	Unknown datatype
                ASSERT( FALSE );
            }
            m_fields[i].SetNull( FALSE != IsFieldStatusNull( i ) );
        }
    }

    //	Allocate data buffer and set the field type
    void		AllocDataBuffer( CDBVariant& varValue, CODBCFieldInfo& fi )

    {
        int	nFieldType = GetCFieldType( fi.m_nSQLType );
        switch ( nFieldType )
        {
        case SQL_C_BIT:
            varValue.m_dwType = DBVT_BOOL;
            break;

        case SQL_C_UTINYINT:
            varValue.m_dwType = DBVT_UCHAR;
            break;

        case SQL_C_SSHORT:
            varValue.m_dwType = DBVT_SHORT;
            break;

        case SQL_C_SLONG:
            varValue.m_dwType = DBVT_LONG;
            break;

        case SQL_C_FLOAT:
            varValue.m_dwType = DBVT_SINGLE;
            break;

        case SQL_C_DOUBLE:
            varValue.m_dwType = DBVT_DOUBLE;
            break;

        case SQL_C_TIMESTAMP:
            varValue.m_pdate = new TIMESTAMP_STRUCT;
            varValue.m_dwType = DBVT_DATE;
            break;

        case SQL_C_CHAR:
            varValue.m_pstring = new CString;
            varValue.m_dwType = DBVT_STRING;
            break;

        case SQL_C_BINARY:
            varValue.m_pbinary = new CLongBinary;
            varValue.m_dwType = DBVT_BINARY;
            break;

        default:
            //	Unknown data type
            ASSERT( FALSE );
        }
    }
    short		GetCFieldType( short nSQLType )

    {
        short nFieldType = 0;

        switch ( nSQLType )
        {
        case SQL_BIT:
            nFieldType = SQL_C_BIT;
            break;

        case SQL_TINYINT:
            nFieldType = SQL_C_UTINYINT;
            break;

        case SQL_SMALLINT:
            nFieldType = SQL_C_SSHORT;
            break;

        case SQL_INTEGER:
            nFieldType = SQL_C_SLONG;
            break;

        case SQL_REAL:
            nFieldType = SQL_C_FLOAT;
            break;

        case SQL_FLOAT:
        case SQL_DOUBLE:
            nFieldType = SQL_C_DOUBLE;
            break;

        case SQL_DATE:
        case SQL_TIME:
        case SQL_TIMESTAMP:
            nFieldType = SQL_C_TIMESTAMP;
            break;

        case SQL_NUMERIC:
        case SQL_DECIMAL:
        case SQL_BIGINT:
        case SQL_CHAR:
        case SQL_VARCHAR:
            nFieldType = SQL_C_CHAR;
            break;

            //	SQL_LONGVARCHAR moved here.
            //	Its default value is SQL_C_CHAR
        case SQL_LONGVARCHAR:
        case SQL_BINARY:
        case SQL_VARBINARY:
        case SQL_LONGVARBINARY:
            nFieldType = SQL_C_BINARY;
            break;

        default:
            //	Unknown data type
            ASSERT( FALSE );
        }

        return	nFieldType;
    }
};
/////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////
//
//	CODBCRecordset class inline methods
//
/////////////////////////////////////////////////////////////////////////////

inline
//	Gets a field by name
CDBField&	CODBCRecordset::Field( LPCTSTR szName )
{
    return	Field( GetFieldID( szName ) );
}

inline
//	Gets a field by number
CDBField&	CODBCRecordset::Field( int nField )
{
    //	There is no allocated storage
    ASSERT( m_fields != NULL );
    //	The field ID is invalid
    ASSERT( nField >= 0 );
    ASSERT( nField < GetODBCFieldCount() );
    //	There is no data in the current recordset position
    ASSERT( IsOpen() );

    return	m_fields[ nField ];
}
////////////////////////////////////////////////////////////////
//	Get field methods

inline
CDBField&	CODBCRecordset::operator()( LPCTSTR szField )
{
    return	Field( szField );
}

inline
CDBField&	CODBCRecordset::operator()( int nField )
{
    return	Field( nField );
}
////////////////////////////////////////////////////////////////
//	Get by Column Name

inline
bool	CODBCRecordset::GetBool( LPCTSTR szName )
{
    return	Field( szName ).AsBool();
}

inline
unsigned char	CODBCRecordset::GetChar( LPCTSTR szName )
{
    return	Field( szName ).AsChar();
}

inline
short	CODBCRecordset::GetShort( LPCTSTR szName )
{
    return	Field( szName ).AsShort();
}

inline
int		CODBCRecordset::GetInt( LPCTSTR szName )
{
    return	Field( szName ).AsInt();
}

inline
long	CODBCRecordset::GetLong( LPCTSTR szName )
{
    return	Field( szName ).AsLong();
}

inline
float	CODBCRecordset::GetFloat( LPCTSTR szName )
{
    return	Field( szName ).AsFloat();
}

inline
double	CODBCRecordset:: GetDouble( LPCTSTR szName )
{
    return	Field( szName ).AsDouble();
}

inline
COleDateTime	CODBCRecordset::GetDate( LPCTSTR szName )
{
    return	Field( szName ).AsDate();
}

inline
CString	CODBCRecordset::GetString( LPCTSTR szName )
{
    return	Field( szName ).AsString();
}

inline
CLongBinary*	CODBCRecordset::GetBinary( LPCTSTR szName )
{
    return	Field( szName ).AsBinary();
}
////////////////////////////////////////////////////////////////
// Get by Column Number

inline
bool	CODBCRecordset::GetBool( int nCol )
{
    return	Field( nCol ).AsBool();
}

inline
unsigned char	CODBCRecordset::GetChar( int nCol )
{
    return	Field( nCol ).AsChar();
}

inline
short	CODBCRecordset::GetShort( int nCol )
{
    return	Field( nCol ).AsShort();
}

inline
int		CODBCRecordset::GetInt( int nCol )
{
    return	Field( nCol ).AsInt();
}

inline
long	CODBCRecordset::GetLong( int nCol )
{
    return	Field( nCol ).AsLong();
}

inline
float	CODBCRecordset::GetFloat( int nCol )
{
    return	Field( nCol ).AsFloat();
}

inline
double	CODBCRecordset:: GetDouble( int nCol )
{
    return	Field( nCol ).AsDouble();
}

inline
COleDateTime	CODBCRecordset::GetDate( int nCol )
{
    return	Field( nCol ).AsDate();
}

inline
CString	CODBCRecordset::GetString( int nCol )
{
    return	Field( nCol ).AsString();
}

inline
CLongBinary*	CODBCRecordset::GetBinary( int nCol )
{
    return	Field( nCol ).AsBinary();
}
////////////////////////////////////////////////////////////////


typedef  CODBCRecordset CSimpleRecordset;
#endif		//		__ODBCRECORDSET_H__
